﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Granite.Modeling;
using System.Runtime.Serialization;
using Granite.DataAnnotations;
using System.Collections.Specialized;
using Granite.Metadata;
using Granite.Modeling.Internals;
using System.Collections.ObjectModel;

namespace Rifts.Models.Characters
{

	[DataContract]
	public class FightingStyle : ChangeTrackingModelBase, IAttributeClass
	{
		int m_Attacks;
		int m_Actions;
		int m_Initiative;
		int m_MeleeStrike;
		ReadOnlyCollection<WeaponProficiencyWithStyle> m_Proficiencies;
		int m_RangedStrike;
		int m_PS;
		int m_DamageFromPS;
		PSType m_PSType;
		//int m_Entangle;
		//int m_FlipThrow;
		int m_AutoDodge;
		int m_MeleeDodge;
		int m_RangedDodge;
		int m_Roll;
		int m_Disarm;
		int m_PullPunch;
		int m_Parry;



		[DataMember]
		public string Name
		{
			get { return Get<string>("Name"); }
			set { Set(value, "Name"); }
		}

		[DataMember]
		public UnarmedAttackCollection UnarmedAttacks
		{
			get { return GetNew<UnarmedAttackCollection>("UnarmedAttacks"); }
		}

		[DataMember]
		public AbilityCollection Abilities
		{
			get { return GetNew<AbilityCollection>("Abilities"); }
		}

		internal void UpdateCalculatedValues(Character character)
		{

			//inherited values
			m_Initiative = character.Initiative;
			m_Attacks = character.Attacks;
			m_Actions = character.Actions;
			m_Initiative = character.Initiative;
			m_MeleeStrike = character.MeleeStrike;
			m_RangedStrike = character.RangedStrike;

			m_PS = character.PS;
			m_PSType = character.PSType;
			m_DamageFromPS = character.DamageFromPS;

			//m_Entangle = character.Entangle;
			//m_FlipThrow = character.FlipThrow;
			m_AutoDodge = character.AutoDodge;
			m_MeleeDodge = character.MeleeDodge;
			m_RangedDodge = character.RangedDodge;
			m_Roll = character.Roll;
			m_PullPunch = character.PullPunch;
			m_Disarm = character.Disarm;
			m_Parry = character.Parry;

			//copy inherited abilities and unarmed attacks
			foreach (var ability in character.CombatAbilities)
			{
				if (!Abilities.Contains(ability.Name))
					Abilities.Add(ability.Clone());
			}

			foreach (var attack in character.UnarmedAttacks)
			{
				if (!UnarmedAttacks.Contains(attack.Name))
					UnarmedAttacks.Add(attack);
			}

			//display the weapon proficiencies
			m_Proficiencies = new ReadOnlyCollection<WeaponProficiencyWithStyle>((from wp in character.Proficiencies select new WeaponProficiencyWithStyle(wp, this)).ToList());

			OnPropertyChanged("");


			//update damage scores

			var hasPowerPunch = Abilities.Contains("Power Punch");
			var hasPowerKick = Abilities.Contains("Power Kick");

			foreach (var attack in UnarmedAttacks)
				attack.Recalculate(m_PS, m_DamageFromPS, m_PSType, hasPowerPunch, hasPowerKick, MeleeStrike, RangedStrike);
		}

		public void AddUnarmedAttack(string name, string basedOn, string damageBase)
		{
			var hasPowerPunch = Abilities.Contains("Power Punch");
			var hasPowerKick = Abilities.Contains("Power Kick");

			var attack = new UnarmedAttack() { Name = name, BasedOn = basedOn, DamageBase = damageBase };
			attack.Recalculate(m_PS, m_DamageFromPS, m_PSType, hasPowerPunch, hasPowerKick, MeleeStrike, RangedStrike);
			UnarmedAttacks.Add(attack);
		}

		[DataMember]
		public int AttacksBase
		{
			get { return Get<int>("AttacksBase"); }
			set { Set(value, "AttacksBase"); }
		}

		[CalculatedField("AttacksBase")]
		public int Attacks
		{
			get { return m_Attacks + AttacksBase; }
		}

		[DataMember]
		public int ActionsBase
		{
			get { return Get<int>("ActionsBase"); }
			set { Set(value, "ActionsBase"); }
		}

		[CalculatedField("ActionsBase")]
		public int Actions
		{
			get { return m_Actions + ActionsBase; }
		}

		[DataMember]
		public int InitiativeBase
		{
			get { return Get<int>("InitiativeBase"); }
			set { Set(value, "InitiativeBase"); }
		}

		[CalculatedField("InitiativeBase")]
		public int Initiative
		{
			get { return m_Initiative + InitiativeBase; }
		}

		[DataMember]
		public int MeleeStrikeBase
		{
			get { return Get<int>("MeleeStrikeBase"); }
			set { Set(value, "MeleeStrikeBase"); }
		}

		[CalculatedField("MeleeStrikeBase")]
		public int MeleeStrike
		{
			get { return m_MeleeStrike + MeleeStrikeBase; }
		}

		[DataMember]
		public int RangedStrikeBase
		{
			get { return Get<int>("RangedStrikeBase"); }
			set { Set(value, "RangedStrikeBase"); }
		}

		[CalculatedField("RangedStrikeBase")]
		public int RangedStrike
		{
			get { return m_RangedStrike + RangedStrikeBase; }
		}

		[DataMember]
		public int EntangleBase
		{
			get { return Get<int>("EntangleBase"); }
			set { Set(value, "EntangleBase"); }
		}

		[CalculatedField("EntangleBase")]
		public int Entangle
		{
			get { return EntangleBase; }
		}

		[DataMember]
		public int FlipThrowBase
		{
			get { return Get<int>("FlipThrowBase"); }
			set { Set(value, "FlipThrowBase"); }
		}

		[CalculatedField("FlipThrowBase")]
		public int FlipThrow
		{
			get { return FlipThrowBase; }
		}

		[DataMember]
		public int AutoDodgeBase
		{
			get { return Get<int>("AutoDodgeBase"); }
			set { Set(value, "AutoDodgeBase"); }
		}

		[CalculatedField("AutoDodgeBase")]
		public int? AutoDodge
		{
			get
			{

				if (Abilities.Contains("Auto Dodge"))
					return m_AutoDodge + AutoDodgeBase;
				else
					return null;
			}
		}

		[DataMember]
		public int MeleeDodgeBase
		{
			get { return Get<int>("MeleeDodgeBase"); }
			set { Set(value, "MeleeDodgeBase"); }
		}

		[CalculatedField("MeleeDodgeBase")]
		public int MeleeDodge
		{
			get { return m_MeleeDodge + MeleeDodgeBase; }
		}

		[DataMember]
		public int RangedDodgeBase
		{
			get { return Get<int>("RangedDodgeBase"); }
			set { Set(value, "RangedDodgeBase"); }
		}

		[CalculatedField("RangedDodgeBase")]
		public int RangedDodge
		{
			get { return m_RangedDodge + RangedDodgeBase; }
		}

		[DataMember]
		public int RollBase
		{
			get { return Get<int>("RollBase"); }
			set { Set(value, "RollBase"); }
		}

		[CalculatedField("RollBase")]
		public int Roll
		{
			get { return m_Roll + RollBase; }
		}

		[DataMember]
		public int PullPunchBase
		{
			get { return Get<int>("PullPunchBase"); }
			set { Set(value, "PullPunchBase"); }
		}

		[CalculatedField("PullPunchBase")]
		public int PullPunch
		{
			get { return m_PullPunch + PullPunchBase; }
		}

		[DataMember]
		public int DisarmBase
		{
			get { return Get<int>("DisarmBase"); }
			set { Set(value, "DisarmBase"); }
		}

		[CalculatedField("DisarmBase")]
		public int Disarm
		{
			get { return m_Disarm + DisarmBase; }
		}

		[DataMember]
		public int ParryBase
		{
			get { return Get<int>("ParryBase"); }
			set { Set(value, "ParryBase"); }
		}

		[CalculatedField("ParryBase")]
		public int Parry
		{
			get { return m_Parry + ParryBase; }
		}


		public void BumpAttribute(string attributeName, int value)
		{
			var propertyName = MapAttribute(attributeName);

			var oldValue = GetAttribute(propertyName);
			var newValue = (oldValue ?? 0) + value;
			Properties.Set(newValue, PropertySetModes.FixCasing | PropertySetModes.RaiseChangedEvent | PropertySetModes.ValidateProperty | PropertySetModes.ValidateObject, propertyName);
		}

		internal int? GetAttribute(string attributeName)
		{
			var propertyName = MapAttribute(attributeName);
			var oldValue = (int?)Properties.Metadata.Properties[propertyName].InvokeGet(this);
			return oldValue;
		}

		static string MapAttribute(string attributeName)
		{
			if (s_AttributeList.Contains(attributeName) && s_AttributeList[attributeName].CanWrite)
				return attributeName;

			string baseProperty = attributeName + "Base";
			if (s_AttributeList.Contains(baseProperty) && s_AttributeList[baseProperty].CanWrite)
				return baseProperty;

			return attributeName; //the error will be handled elsewhere
		}

		readonly static PropertyMetadataCollection s_AttributeList = MetadataCache.GetMetadata(typeof(FightingStyle)).Properties;


		public int DamageFromPS
		{
			get { return m_DamageFromPS; }
		}

		public ReadOnlyCollection<WeaponProficiencyWithStyle> Proficiencies
		{
			get { return m_Proficiencies; }
		}

		public void SetAttribute(string attributeName, int value)
		{
			var propertyName = MapAttribute(attributeName);

			if (Properties.Metadata.Properties.Contains(propertyName))
				Properties.Set(value, PropertySetModes.FixCasing | PropertySetModes.RaiseChangedEvent | PropertySetModes.ValidateProperty | PropertySetModes.ValidateObject, propertyName);
		}

	}
}
